package com.xrigau.gametest;

import org.andengine.engine.Engine;
import org.andengine.engine.camera.ZoomCamera;
import org.andengine.engine.handler.physics.PhysicsHandler;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.modifier.MoveModifier;
import org.andengine.entity.scene.IOnSceneTouchListener;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.scene.menu.MenuScene;
import org.andengine.entity.scene.menu.item.SpriteMenuItem;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.extension.tmx.TMXLayer;
import org.andengine.extension.tmx.TMXTile;
import org.andengine.extension.tmx.TMXTiledMap;
import org.andengine.input.touch.TouchEvent;
import org.andengine.input.touch.detector.PinchZoomDetector;
import org.andengine.input.touch.detector.PinchZoomDetector.IPinchZoomDetectorListener;
import org.andengine.input.touch.detector.ScrollDetector;
import org.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
import org.andengine.input.touch.detector.SurfaceScrollDetector;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.atlas.bitmap.BuildableBitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.source.IBitmapTextureAtlasSource;
import org.andengine.opengl.texture.atlas.buildable.builder.BlackPawnTextureAtlasBuilder;
import org.andengine.opengl.texture.atlas.buildable.builder.ITextureAtlasBuilder.TextureAtlasBuilderException;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.ui.activity.BaseGameActivity;
import org.andengine.util.modifier.ease.EaseExponentialInOut;

import android.opengl.GLES20;
import android.os.Handler;
import android.util.DisplayMetrics;

import com.xrigau.gametest.log.IErrorLog;
import com.xrigau.gametest.log.Logging;
import com.xrigau.gametest.menu.TopMenuAnimator;

public class GameActivity extends BaseGameActivity implements IOnSceneTouchListener, IScrollDetectorListener, IPinchZoomDetectorListener {

	private static final int MENU_DELAY_TIME = 2000;
	private final String TAG = "GameActivity";
	private final float MAX_ZOOM = 1f;
	protected static final int MENU_RESET = 0;
	protected static final int MENU_QUIT = MENU_RESET + 1;
	
	// Camera stuff
	public int CAMERA_WIDTH = 800;
	public int CAMERA_HEIGHT = 480;
	public ZoomCamera mCamera;
	public ScrollDetector mScrollDetector;
	public PinchZoomDetector mPinchZoomDetector;
	public float mPinchZoomStartedCameraZoomFactor;
	private final float zoomDepth = 5; // Smaller this is, the less we zoom in?
	private boolean mClicked = false;

	public Engine mEngine;
	public IErrorLog log = null;
	private Scene mScene;
	private Handler mHandler = new Handler();
	
	// Map and current layer.
	private MapHandler mMapHandler;
	public TMXLayer currentLayer = null;
	public TMXTiledMap mMap = null;

	private BuildableBitmapTextureAtlas mBitmapTextureAtlas;
	private TiledTextureRegion mCarTextureRegion;
	
	protected MenuScene mMenuScene;

	private BitmapTextureAtlas mMenuTexture;
	protected ITextureRegion mMenuResetTextureRegion;
	protected ITextureRegion mMenuQuitTextureRegion;
	
	@Override
	public EngineOptions onCreateEngineOptions() {
		this.log = new Logging();
		this.log.setTag(this.TAG);
		this.log.enable(false);
		this.log.i(0, "onCreateEngineOptions");
		final DisplayMetrics displayMetrics = new DisplayMetrics();
		this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
		CAMERA_WIDTH = displayMetrics.widthPixels;
		CAMERA_HEIGHT = displayMetrics.heightPixels;
		this.mCamera = new ZoomCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		EngineOptions eOps = new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mCamera);
		return eOps;
	}

	@Override
	public Engine onCreateEngine(EngineOptions pEngineOptions) {
		this.log.i(0, "onCreateEngine");
		this.mEngine = new Engine(pEngineOptions);
		return this.mEngine;
	}

	@Override
	public void onCreateResources(OnCreateResourcesCallback pOnCreateResourcesCallback) throws Exception {
		this.log.i(0, "onCreateResources");
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");

		this.mBitmapTextureAtlas = new BuildableBitmapTextureAtlas(this.getTextureManager(), 512, 256, TextureOptions.NEAREST);
		this.mCarTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBitmapTextureAtlas, this, "taxi-01.png", 2, 1);
		
		this.mScrollDetector = new SurfaceScrollDetector(this);
		this.mPinchZoomDetector = new PinchZoomDetector(this);
		this.mMapHandler = new MapHandler(this);
		
		try {
			this.mBitmapTextureAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 0, 1));
			this.mBitmapTextureAtlas.load();
			
			this.mMenuTexture = new BitmapTextureAtlas(this.getTextureManager(), 512, 256, TextureOptions.BILINEAR);
			this.mMenuResetTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mMenuTexture, this, "menu_reset.png", 0, 0);
			this.mMenuQuitTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mMenuTexture, this, "menu_quit.png", 0, 50);
			this.mMenuTexture.load();
		} catch (TextureAtlasBuilderException e) {
			this.log.e(0, e.getMessage());
		}
		
		pOnCreateResourcesCallback.onCreateResourcesFinished();
	}

	@Override
	public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback) throws Exception {
		this.log.i(0, "onCreateScene");
		mScene = new Scene();
		mScene.setBackground(new Background(0.2980392156862745f, 0.2784313725490196f, 0.2705882352941176f));
		this.createMenuScene();
		pOnCreateSceneCallback.onCreateSceneFinished(mScene);
	}

	@Override
	public void onPopulateScene(Scene pScene, OnPopulateSceneCallback pOnPopulateSceneCallback) throws Exception {
		this.log.i(0, "onPopulateScene");
		pScene.setOnSceneTouchListener(this);
		pScene.setTouchAreaBindingOnActionMoveEnabled(true);
		pScene.setOnAreaTouchTraversalFrontToBack();
		this.mMapHandler.loadMap();
		
		/* Funny taxi. */
		final AnimatedSprite taxi = new AnimatedSprite(-840, 1300, this.mCarTextureRegion, this.getVertexBufferObjectManager());
		final PhysicsHandler physicsHandler = new PhysicsHandler(taxi);
		taxi.registerUpdateHandler(physicsHandler);
		taxi.animate(100);
		physicsHandler.setVelocity(50, -30);
//		taxi.registerEntityModifier(new MoveModifier(30, 0, CAMERA_WIDTH - taxi.getWidth(), 0, CAMERA_HEIGHT - taxi.getHeight()));
		pScene.attachChild(taxi);
		this.mMapHandler.attachAllObjectLayers();
		
		pOnPopulateSceneCallback.onPopulateSceneFinished();
	}

	@Override
	public void onPinchZoomStarted(PinchZoomDetector pPinchZoomDetector, TouchEvent pSceneTouchEvent) {
		this.log.i(1, "onPinchZoomStarted");
		this.mPinchZoomStartedCameraZoomFactor = this.mCamera.getZoomFactor();
		this.mClicked = false;
	}

	@Override
	public void onPinchZoom(PinchZoomDetector pPinchZoomDetector, TouchEvent pTouchEvent, float pZoomFactor) {
		this.log.i(1, "onPinchZoom");
		this.mCamera.setZoomFactor(Math.min(Math.max(this.MAX_ZOOM, this.mPinchZoomStartedCameraZoomFactor * pZoomFactor), this.zoomDepth));
		this.mClicked = false;
	}

	@Override
	public void onPinchZoomFinished(PinchZoomDetector pPinchZoomDetector, TouchEvent pTouchEvent, float pZoomFactor) {
		this.log.i(1, "onPinchZoomFinished");
		this.mClicked = false;
	}

	@Override
	public void onScrollStarted(ScrollDetector pScollDetector, int pPointerID, float pDistanceX, float pDistanceY) {
		this.log.i(1, "onScrollStarted");
	}

	@Override
	public void onScroll(ScrollDetector pScollDetector, int pPointerID, float pDistanceX, float pDistanceY) {
		this.log.i(1, "onScroll");
		final float zoomFactor = mCamera.getZoomFactor();
		float xLocation = -pDistanceX / zoomFactor;
		float yLocation = -pDistanceY / zoomFactor;
		mCamera.offsetCenter(xLocation, yLocation);
		this.mClicked = false;
	}

	@Override
	public void onScrollFinished(ScrollDetector pScollDetector, int pPointerID, float pDistanceX, float pDistanceY) {
		this.log.i(1, "onScrollFinished");
	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
		this.log.i(2, "onSceneTouchEvent");

		if (pSceneTouchEvent.isActionDown()) {
			mHandler.removeCallbacks(mHideMenuRunnable);
			
			if (!mScene.hasChildScene()) {
				/* Attach the menu. */
				this.mScene.setChildScene(this.mMenuScene, false, false, false);
			}
		}
		
		if (this.mPinchZoomDetector != null) {
			this.mPinchZoomDetector.onTouchEvent(pSceneTouchEvent);
			if (this.mPinchZoomDetector.isZooming()) {
				this.mScrollDetector.setEnabled(false);
			} else {
				if (pSceneTouchEvent.isActionDown()) {
					this.mScrollDetector.setEnabled(true);
				}
				this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
			}
		} else {
			this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
		}

		if (pSceneTouchEvent.isActionUp()) {
			if (this.mClicked) {
				this.handleActionDown(pScene, pSceneTouchEvent);
			}
			this.mClicked = true;
			mHandler.postDelayed(mHideMenuRunnable, MENU_DELAY_TIME);
		}
		return true;
	}

	private final Runnable mHideMenuRunnable = new Runnable() {
		@Override
		public void run() {
			if (mScene.hasChildScene()) {
				/* Remove the menu and reset it. */
				mMenuScene.back();
			}
		}
	};
	
	private void handleActionDown(Scene pScene, TouchEvent pSceneTouchEvent) {
		this.log.i(4, String.format("Touch X: %f Y: %f", pSceneTouchEvent.getX(), pSceneTouchEvent.getY()));
		this.touchMap(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());

	}

	public void resetCamera() {
		this.log.i(4, "Reset camera");
		this.mCamera.setZoomFactor(MAX_ZOOM);
		this.mCamera.setCenter(0, 0);
	}

	public void setupCameraIsometric(final float height, final float width) {
		this.log.i(4, "Setup camera");

		final float MAX_CAMERA_BOUND_ADDITION = 60;
		final float halfTileWidth = this.mMap.getTileWidth() / 2;
		final float xMin = this.mMap.getTileRows() * halfTileWidth;
		final float xMax = this.mMap.getTileColumns() * halfTileWidth;
		final float pBoundsXMin = halfTileWidth - xMin - MAX_CAMERA_BOUND_ADDITION;
		final float pBoundsYMin = -MAX_CAMERA_BOUND_ADDITION;
		final float pBoundsXMax = halfTileWidth + xMax + MAX_CAMERA_BOUND_ADDITION;
		final float pBoundsYMax = height + MAX_CAMERA_BOUND_ADDITION;
		this.mCamera.setBounds(pBoundsXMin, pBoundsYMin, pBoundsXMax, pBoundsYMax);
		this.mCamera.setBoundsEnabled(true);
		this.resetCamera();
	}

	public void touchMap(final float pX, final float pY) {
		// Standard method of getting tile
		final float[] pToTiles = this.getEngine().getScene().convertLocalToSceneCoordinates(pX, pY);
		this.currentLayer = this.mMap.getTMXLayers().get(0);
		final TMXTile tmxSelected = this.currentLayer.getTMXTileAt(pToTiles[0], pToTiles[1]);
	}

	public void setMap(TMXTiledMap map) {
		mMap = map;
	}
	
	protected void createMenuScene() {
		this.mMenuScene = new MenuScene(this.mCamera);
		this.mMenuScene.setMenuAnimator(new TopMenuAnimator(EaseExponentialInOut.getInstance()));
		
		final SpriteMenuItem resetMenuItem = new SpriteMenuItem(MENU_RESET,
				this.mMenuResetTextureRegion,
				this.getVertexBufferObjectManager());
		resetMenuItem.setBlendFunction(GLES20.GL_SRC_ALPHA,
				GLES20.GL_ONE_MINUS_SRC_ALPHA);
		this.mMenuScene.addMenuItem(resetMenuItem);

		final SpriteMenuItem quitMenuItem = new SpriteMenuItem(MENU_QUIT,
				this.mMenuQuitTextureRegion,
				this.getVertexBufferObjectManager());
		quitMenuItem.setBlendFunction(GLES20.GL_SRC_ALPHA,
				GLES20.GL_ONE_MINUS_SRC_ALPHA);
		this.mMenuScene.addMenuItem(quitMenuItem);

		this.mMenuScene.buildAnimations();

		this.mMenuScene.setBackgroundEnabled(false);
//		this.mMenuScene.setOnMenuItemClickListener(this);
	}
}